package
{
	import Box2D.Collision.Shapes.b2CircleShape;
	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2DebugDraw;
	import Box2D.Dynamics.b2FixtureDef;
	import Box2D.Dynamics.b2World;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	
	import net.hires.debug.Stats;
	
	[SWF(width=1280,height=722,frameRate=60)]
	public class Box2DTest extends Sprite
	{
		private var world:b2World;  
		private var timestep:Number;  
		private var iterations:uint;  
		private var pixelsPerMeter:Number = 30;  
		public function Box2DTest()
		{
			makeWorld();  
			makeWalls();  
			//makeDebugDraw();  
			
			makeABunchOfDynamicBodies();
			
			if (stage) init();  
			else addEventListener(Event.ADDED_TO_STAGE, init);  
			
			
			var s:Stats = new Stats();
			s.scaleX = 3;
			s.scaleY = 3;
			addChild(s);
		}
		private function makeWorld():void  
		{  
			// Define the gravity vector  
			var gravity:b2Vec2 = new b2Vec2(0.0, 10.0);  
			// Allow bodies to sleep  
			var doSleep:Boolean = true;  
			// Construct a world object  
			world = new b2World(gravity, doSleep);  
			world.SetWarmStarting(true);  
			timestep = 1.0 / 30.0;  
			iterations = 10;  
			
		}  
		private function getRectBM(w:int,h:int,c:uint=0x0):Sprite
		{
			var rect:Sprite = new Sprite();
			var spr:Sprite = new Sprite();
			spr.graphics.beginFill(c);
			spr.graphics.drawRect(0,0,w,h);
			spr.graphics.endFill();
			var bm:Bitmap = new Bitmap();
			bm.bitmapData = new BitmapData(w,h);
			bm.bitmapData.draw(spr);
			bm.x = -w/2;
			bm.y = -h/2;
			rect.addChild(bm);
			addChild(rect);
			return rect;
		}
		private function makeWalls():void  
		{  
			// Note: I am assuming a stage size of 1280*722;    640*361; 
			// These placements will put a row of boxes around an area that size.  
			
			// We reuse the shape and Body Definitions.  
			// Box2D creates a different body each time we call world.CreateBody(wallBd);  
			var wall:b2PolygonShape= new b2PolygonShape();  
			var wallBd:b2BodyDef = new b2BodyDef();  
			var wallB:b2Body;  
			
			// Left  
			wallBd.position.Set( -95/pixelsPerMeter,722/pixelsPerMeter/2);  
			wall.SetAsBox(100/pixelsPerMeter, 722/pixelsPerMeter/2);  
			wallB = world.CreateBody(wallBd); // Box2D handles the creation of a new b2Body for us.  
			wallB.CreateFixture2(wall);  
			
			wallB.SetUserData(getRectBM(200,722));
			// Right  
			wallBd.position.Set((1280 + 95) / pixelsPerMeter, 722 / pixelsPerMeter / 2);  
			wallB = world.CreateBody(wallBd);  
			wallB.CreateFixture2(wall);  
			wallB.SetUserData(getRectBM(200,722));
			// Top  
			wallBd.position.Set(1280 / pixelsPerMeter / 2, -95 / pixelsPerMeter);  
			wall.SetAsBox(1280/pixelsPerMeter/2, 100/pixelsPerMeter);  
			wallB = world.CreateBody(wallBd);  
			wallB.CreateFixture2(wall);  
			wallB.SetUserData(getRectBM(1280,200));
			// Bottom  
			wallBd.position.Set(1280 / pixelsPerMeter / 2, (722 + 95) / pixelsPerMeter);  
			wallB = world.CreateBody(wallBd);  
			wallB.CreateFixture2(wall);  
			wallB.SetUserData(getRectBM(1280,200));
			
		}  
		private function makeDebugDraw():void  
		{  
			// set debug draw  
			var debugDraw:b2DebugDraw = new b2DebugDraw();  
			var debugSprite:Sprite = new Sprite();  
			addChild(debugSprite);  
			debugDraw.SetSprite(debugSprite);  
			debugDraw.SetDrawScale(30.0);  
			debugDraw.SetFillAlpha(0.3);  
			debugDraw.SetLineThickness(1.0);  
			debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);  
			world.SetDebugDraw(debugDraw);  
		}  
		
		private function init(e:Event = null):void  
		{  
			removeEventListener(Event.ADDED_TO_STAGE, init);  
			// entry point  
			update()  
			
			addEventListener(Event.ENTER_FRAME, update);
		}  
		
		private function update(e:Event = null):void  
		{  
			world.Step(timestep, iterations, iterations);  
			world.ClearForces();  
			
			for (var bb:b2Body = world.GetBodyList(); bb; bb = bb.GetNext()) {
				if (bb.GetUserData()is Sprite) {
					bb.GetUserData().x = bb.GetPosition().x * pixelsPerMeter;
					bb.GetUserData().y = bb.GetPosition().y * pixelsPerMeter;
					bb.GetUserData().rotation = bb.GetAngle() * (180 / Math.PI);
				}
			}
			// Render  
			world.DrawDebugData();  
		}  
		private function makeABunchOfDynamicBodies():void  
		{  
			var i:int  
			var body:b2Body;  
			var fd:b2FixtureDef;
			var w:int;
			var h:int;
			//Add rectangles  
			for (i = 0; i < 5; i++){  
				var bodyDef:b2BodyDef = new b2BodyDef();  
				bodyDef.type = b2Body.b2_dynamicBody;  
				var boxDef:b2PolygonShape = new b2PolygonShape();  
				fd = new b2FixtureDef();  
				fd.shape = boxDef;  
				fd.density = 1.0;  
				// Override the default friction.  
				fd.friction = 0.3;  
				fd.restitution = 0.1;  
				w = (Math.random() * 5 + 10);
				h = (Math.random() * 5 + 10);
				boxDef.SetAsBox(w / pixelsPerMeter, h / pixelsPerMeter);  
				bodyDef.position.Set((Math.random() * 400 + 120) / pixelsPerMeter, (Math.random() * 150 + 50) / pixelsPerMeter);  
				bodyDef.angle = Math.random() * Math.PI;  
				body = world.CreateBody(bodyDef);  
				body.CreateFixture(fd);  
				
				body.SetUserData(getRectBM(w*2,h*2,0xffffff*Math.random()));
			}  
			return;
			// Add Circles  
			for (i = 0; i < 5; i++){  
				var bodyDefC:b2BodyDef = new b2BodyDef();  
				bodyDefC.type = b2Body.b2_dynamicBody;  
				var circDef:b2CircleShape= new b2CircleShape((Math.random() * 5 + 10) / pixelsPerMeter);  
				fd = new b2FixtureDef();  
				fd.shape = circDef;  
				fd.density = 1.0;  
				// Override the default friction.  
				fd.friction = 0.3;  
				fd.restitution = 0.1;  
				bodyDefC.position.Set((Math.random() * 400 + 120) / pixelsPerMeter, (Math.random() * 150 + 50) / pixelsPerMeter);  
				bodyDefC.angle = Math.random() * Math.PI;  
				body = world.CreateBody(bodyDefC);  
				body.CreateFixture(fd);  
				
			}  
			// Add irregular convex polygons  
			
			for (i = 0; i < 15; i++){  
				var bodyDefP:b2BodyDef = new b2BodyDef();  
				bodyDefP.type = b2Body.b2_dynamicBody;  
				var polyDef:b2PolygonShape = new b2PolygonShape();  
				// Draw a polygon point by point  
				if (Math.random() > 0.66) {  
					polyDef.SetAsArray([  
						new b2Vec2((-10 -Math.random()*10) / pixelsPerMeter, ( 10 +Math.random()*10) / pixelsPerMeter),  
						new b2Vec2(( -5 -Math.random()*10) / pixelsPerMeter, (-10 -Math.random()*10) / pixelsPerMeter),  
						new b2Vec2((  5 +Math.random()*10) / pixelsPerMeter, (-10 -Math.random()*10) / pixelsPerMeter),  
						new b2Vec2(( 10 +Math.random() * 10) / pixelsPerMeter, ( 10 +Math.random() * 10) / pixelsPerMeter)  
					]);  
				}  
				else if (Math.random() > 0.5)  
				{  
					var array:Array = [];  
					array[0] = new b2Vec2(0, (10 +Math.random()*10) / pixelsPerMeter);  
					array[2] = new b2Vec2((-5 -Math.random()*10) / pixelsPerMeter, (-10 -Math.random()*10) / pixelsPerMeter);  
					array[3] = new b2Vec2(( 5 +Math.random()*10) / pixelsPerMeter, (-10 -Math.random()*10) / pixelsPerMeter);  
					array[1] = new b2Vec2((array[0].x + array[2].x), (array[0].y + array[2].y));  
					array[1].Multiply(Math.random()/2+0.8);  
					array[4] = new b2Vec2((array[3].x + array[0].x), (array[3].y + array[0].y));  
					array[4].Multiply(Math.random() / 2 + 0.8);  
					polyDef.SetAsArray(array);  
				}  
				else  
				{  
					polyDef.SetAsArray([  
						new b2Vec2(0, (10 +Math.random()*10) / pixelsPerMeter),  
						new b2Vec2((-5 -Math.random()*10) / pixelsPerMeter, (-10 -Math.random()*10) / pixelsPerMeter),  
						new b2Vec2(( 5 +Math.random() * 10) / pixelsPerMeter, ( -10 -Math.random() * 10) / pixelsPerMeter)  
					]);  
				}  
				fd = new b2FixtureDef();  
				fd.shape = polyDef;  
				fd.density = 1.0;  
				fd.friction = 0.3;  
				fd.restitution = 0.1;  
				bodyDefP.position.Set((Math.random() * 400 + 120) / pixelsPerMeter, (Math.random() * 150 + 50) / pixelsPerMeter);  
				bodyDefP.angle = Math.random() * Math.PI;  
				body = world.CreateBody(bodyDefP);  
				body.CreateFixture(fd);  
			}  
		}  
	}
}